using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace ThingsGateway.Foundation
{
    /// <summary>
    /// 日志基类
    /// </summary>
    public abstract class LogNetFileBase : LogNetBase
    {
        /// <summary>
        /// 控制台日志输出类型。
        /// 当<see cref="Log(LogType, string, string)"/>的类型，在该设置之内时，才会真正输出日志。
        /// </summary>
        public LogType ConsoleLogType { get; set; }
        /// <summary>
        /// 文件日志输出类型。
        /// 当<see cref="Log(LogType, string, string)"/>的类型，在该设置之内时，才会真正输出日志。
        /// </summary>
        public LogType SaveLogType { get; set; }
        private EasyLock fileSaveLock;
        private string lastLogSaveFileName = string.Empty;
        private int m_SaveStatus = 0;

        /// <summary>
        /// 日志基类，实现日志存储
        /// </summary>
        public LogNetFileBase()
        {
            SaveLogType = All;
            ConsoleLogType = All;
            logMessageItems = new IntelligentConcurrentQueue<LogMessageItem>(100000000);
            filterKeyword = new IntelligentConcurrentList<string>();
            fileSaveLock = new EasyLock();
        }

        protected string FilePath { get; set; } = string.Empty;
        /// <summary>
        /// 文件的数量，大于1生效。<br />
        /// </summary>
        protected int FileQuantity { get; set; } = -1;

        /// <inheritdoc></inheritdoc>
        public abstract string GetFileSaveName();

        public virtual string[] GetLogFileNames()
        {
            return !string.IsNullOrEmpty(FilePath) ? Directory.GetFiles(FilePath, "Logs_*.txt") : new string[0];
        }

        /// <summary>
        /// 日志写入完成时,删除多余文件
        /// </summary>
        /// <inheritdoc />
        protected virtual void OnWriteCompleted(bool createNewLogFile)
        {
            if (!createNewLogFile || this.FileQuantity <= 1)
                return;
            try
            {
                string[] existLogFileNames = GetLogFileNames();
                if (existLogFileNames.Length > FileQuantity)
                {
                    List<FileInfo> fileInfoList = new List<FileInfo>();
                    for (int index = 0; index < existLogFileNames.Length; ++index)
                    {
                        fileInfoList.Add(new FileInfo(existLogFileNames[index]));
                    }

                    fileInfoList.Sort((m, n) => m.CreationTime.CompareTo(n.CreationTime));
                    for (int index = 0; index < fileInfoList.Count - FileQuantity; ++index)
                    {
                        File.Delete(fileInfoList[index].FullName);
                    }
                }
            }
            catch
            {
            }
        }

        protected override void StartOut()
        {
            if (Interlocked.CompareExchange(ref m_SaveStatus, 1, 0) != 0)
            {
                return;
            }
            Task.Run(() => TaskSaveFile());
        }

        private void TaskSaveFile()
        {
            this.fileSaveLock.Lock();
            string fileSaveName = this.GetFileSaveName();
            bool createNewLogFile = false;
            if (!string.IsNullOrEmpty(fileSaveName))
            {
                if (fileSaveName != this.lastLogSaveFileName)
                {
                    createNewLogFile = true;
                    this.lastLogSaveFileName = fileSaveName;
                }
                StreamWriter streamWriter = (StreamWriter)null;
                LogMessageItem logMessageItem = this.GetQueueLogItem();
                try
                {
                    streamWriter = new StreamWriter(fileSaveName, true, Encoding.UTF8);
                    while (logMessageItem != null)
                    {
                        if (ConsoleLogType.HasFlag(logMessageItem.Degree))
                            this.ConsoleWriteLog(logMessageItem);
                        InvokeBeforeOut(this, logMessageItem);
                        if (SaveLogType.HasFlag(logMessageItem.Degree))
                        {
                            bool flag = true;
                            if (!logMessageItem.KeyWord.IsNullOrEmptyEx())
                            {
                                flag = !this.filterKeyword.Contains(logMessageItem.KeyWord);
                            }
                            if (logMessageItem.Cancel)
                                flag = false;
                            if (flag)
                            {
                                streamWriter.Write(logMessageItem);
                                streamWriter.Write(Environment.NewLine);
                                streamWriter.Flush();
                            }
                        }
                        logMessageItem = this.GetQueueLogItem();
                    }
                }
                catch (Exception ex)
                {
                    this.AddLogItem(
            logMessageItem,
            new LogMessageItem()
            {
                Degree = LogType.Fatal,
                Text = LogNetExtensions.GetStringFromException("LogNet", ex)
            }
                      );
                }
                finally
                {
                    streamWriter?.Dispose();
                }

            }
            else
            {
                LogMessageItem logMessageItem = this.GetQueueLogItem();
                if (ConsoleLogType.HasFlag(logMessageItem.Degree))
                    this.ConsoleWriteLog(logMessageItem);
                InvokeBeforeOut(this, logMessageItem);
            }

            this.fileSaveLock.UnLock();
            Interlocked.Exchange(ref this.m_SaveStatus, 0);
            this.OnWriteCompleted(createNewLogFile);
            if (this.logMessageItems.Count <= 0)
                return;
            this.StartOut();
        }

    }
}
